/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package replica_manager_core;

import txml.TransactionXmlLogger;
import txml.TransactionXmlLoggerException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.LinkedList;
import javax.ejb.Stateless;
import javax.ejb.LocalBean;
import javax.xml.rpc.ServiceException;
import replica_manager_client.ReplicaManagerAccessor;
import transaction_manager_client.TransactionManagerAccessor;

import org.w3c.dom.*;
import javax.xml.parsers.*;

/**
 *
 * @author iMarshall
 */
@Stateless
@LocalBean
public class ReplicaManagerCore {

    private String[] neighbours;
    private String selfEndpoint;

    private TransactionXmlLogger logger;

    public ReplicaManagerCore() {

        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse("taskman_config.xml");
            Element root = doc.getDocumentElement();

            NodeList ipaddrs = root.getElementsByTagName("IpAddress");
            NodeList ports = root.getElementsByTagName("WsdlPort");

            selfEndpoint = ((Element) ipaddrs.item(0)).getTextContent()
                + ":"
                + ((Element) ports.item(0)).getTextContent();

            LinkedList<String> l = new LinkedList<String>();
            for (int i = 1; i < ipaddrs.getLength(); i++) {
                l.add(((Element) ipaddrs.item(i)).getTextContent()
                + ":"
                + ((Element) ports.item(i)).getTextContent());
            }

            neighbours = l.toArray(new String[0]);

        } catch (Exception e) {
            System.err.println("error in constructor replica manager");
        }


    }

    public boolean retrieveReplica()
    {
        BufferedWriter bf = null;
        
        TransactionManagerAccessor tma =
            new TransactionManagerAccessor("http://" + selfEndpoint + "/TransactionManager-war/TransactionManagerWSService");

        try {
            String tID = tma.openTransaction();

            // create a logger
            logger = new TransactionXmlLogger("translog_retrieve", tID);
            // write down the brand new transaction id
            logger.setTransactionStatus(TransactionXmlLogger.TransactionStatus.TRANSACTION_ACTIVE);
            logger.addValue("tID", "", tID);

            for (String neigh : neighbours ) {
                String remoteEndpoint = "http://" + neigh + "/ReplicaManager-war/ReplicaManagerWSService";

                ReplicaManagerAccessor rma =
                    new ReplicaManagerAccessor(remoteEndpoint);

                // if at least one pushReplica() fails, abort transaction
                if (rma.getReplica(tID) == false) {
                    return tma.abortTransaction(tID);
                }
                //System.err.println(neigh);
            }

            String remoteEndpoint = "http://" + getBestNeighbour() + "/ReplicaManager-war/ReplicaManagerWSService";

            ReplicaManagerAccessor rma =
                new ReplicaManagerAccessor(remoteEndpoint);

            logger.addValue("Transmit", "", remoteEndpoint);

            String resource = null;

            try {
                resource = rma.transmitReplica(tID);
            } catch (RemoteException e) {
                e.printStackTrace();
                tma.abortTransaction(tID);

            } catch (ServiceException e) {
                e.printStackTrace();
                tma.abortTransaction(tID);
            }

            logger.addValue("Resource", "resource.xml", "resource.xml.old");

            // copy original file
            File f = new File("resource.xml");
            if (f.exists())
                f.renameTo(new File("resource.xml.old"));

            // write resource
            bf = new BufferedWriter(new FileWriter(new File("resource.xml")));
            bf.write(resource);

            if (tma.closeTransaction(tID) == false) {
                System.err.println("ERROR: close transaction");
                System.exit(1);
                return false;
            }

            logger.setTransactionStatus(TransactionXmlLogger.TransactionStatus.TRANSACTION_COMMITTED);

            return true;

        } catch (RemoteException e) {
            e.printStackTrace();

        } catch (ServiceException e) {
            e.printStackTrace();

        } catch (TransactionXmlLoggerException e) {
            e.printStackTrace();

        } catch (IOException e) {
            e.printStackTrace();
            
        } finally {
            try {
                bf.close();
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        return false;
    }

    public String transmitReplica(String tID)
    {
        BufferedReader br = null;
        try {

            if (logger == null)
                logger = new TransactionXmlLogger("translog", tID);

            String[] chunk = tID.split("_");

            logger.addValue("Transmit", "", chunk[0]);

            StringBuilder sb = new StringBuilder();
            br = new BufferedReader(new FileReader(new File("resource.xml")));

            String leggo;
            while((leggo = br.readLine())!= null){
                sb.append(leggo);
                sb.append(System.getProperty("line.separator"));
            }

            return sb.toString();

        } catch (FileNotFoundException ex) {
            ex.printStackTrace();

        } catch (IOException ex) {
            ex.printStackTrace();

        } catch (TransactionXmlLoggerException ex) {
            ex.printStackTrace();

        } finally {
            try {
                br.close();

            } catch (IOException ex) {
                ex.printStackTrace();
            }
        }

        return null;
    }

    public boolean submitReplica(String resource)
    {
        //System.err.println("DEBUG::submitReplica(" + resource + ")");
     
        TransactionManagerAccessor tma =
            new TransactionManagerAccessor("http://" + selfEndpoint + "/TransactionManager-war/TransactionManagerWSService");

        try {
            String tID = tma.openTransaction();

            // create a logger 
            logger = new TransactionXmlLogger("translog_submit", tID);
            // write down the brand new transaction id
            logger.setTransactionStatus(TransactionXmlLogger.TransactionStatus.TRANSACTION_ACTIVE);
            logger.addValue("tID", "", tID);

            //System.err.println("DEBUG::tid: " + tID);

            for (String neigh : neighbours ) {
                String remoteEndpoint = "http://" + neigh + "/ReplicaManager-war/ReplicaManagerWSService";

                ReplicaManagerAccessor rma =
                    new ReplicaManagerAccessor(remoteEndpoint);

                // if at least one pushReplica() fails, abort transaction
                if (rma.pushReplica(resource, tID) == false) {
                    return tma.abortTransaction(tID);
                }
                //System.err.println(neigh);
            }

            //System.err.println("DEBUG:: calling closeTransaction()");
            if (tma.closeTransaction(tID) == false) {
                System.err.println("ERROR: close transaction");
                System.exit(1);
                return false;
            }

            logger.setTransactionStatus(TransactionXmlLogger.TransactionStatus.TRANSACTION_COMMITTED);

            return true;

        } catch (RemoteException e) {
            e.printStackTrace();
            
        } catch (ServiceException e) {
            e.printStackTrace();

        } catch (TransactionXmlLoggerException e) {
            e.printStackTrace();
        }

        return false;
    }

    public boolean getReplica(String tID)
    {
        String[] chunk = tID.split("_");
        String remoteEndpoint = "http://" + chunk[0] + "/TransactionManager-war/TransactionManagerWSService";

        TransactionManagerAccessor tma =
            new TransactionManagerAccessor(remoteEndpoint);

        try {
            if (tma.join(tID, selfEndpoint) == false)
                return false;
            
            logger = new TransactionXmlLogger("translog", tID);

            logger.setTransactionStatus(TransactionXmlLogger.TransactionStatus.TRANSACTION_ACTIVE);
            logger.addValue("tID", "", tID);

            return true;
            
        } catch (RemoteException re) {
            re.printStackTrace();

        } catch (ServiceException se) {
            se.printStackTrace();

        } catch (TransactionXmlLoggerException ex) {
            ex.printStackTrace();
        }
        
        return false;
    }

    public boolean pushReplica(String resource, String tID)
    {
        //System.err.println("DEBUG::pushReplica(" + resource + ", " + tID + ")");

        // call join to transaction manager
        String[] chunk = tID.split("_");
        String remoteEndpoint = "http://" + chunk[0] + "/TransactionManager-war/TransactionManagerWSService";

        TransactionManagerAccessor tma =
            new TransactionManagerAccessor(remoteEndpoint);

        try {
            if (tma.join(tID, selfEndpoint) == false)
                return false;
            
            logger = new TransactionXmlLogger("translog", tID);

            logger.setTransactionStatus(TransactionXmlLogger.TransactionStatus.TRANSACTION_ACTIVE);
            logger.addValue("tID", "", tID);
            logger.addValue("Resource", "resource.xml", "resource.xml.old");

            // copy original file
            File f = new File("resource.xml");
            if (f.exists())
                f.renameTo(new File("resource.xml.old"));

            // write resource
            BufferedWriter bf = new BufferedWriter(new FileWriter(new File("resource.xml")));
            bf.write(resource);
            bf.close();

            return true;

        } catch (RemoteException re) {
            re.printStackTrace();
            
        } catch (ServiceException se) {
            se.printStackTrace();

        } catch (IOException ioe) {
            ioe.printStackTrace();

        } catch (TransactionXmlLoggerException ex) {
            ex.printStackTrace();
        }

        return false;
    }

    public boolean rollBack(String tID)
    {
        if (logger == null)
            logger = new TransactionXmlLogger("translog", tID);

        try {
            logger.setTransactionStatus(TransactionXmlLogger.TransactionStatus.TRANSACTION_ABORTED);

            File f = new File("resource.xml");
            if (f.exists() && f.canWrite())
                f.delete();

            f = new File("resource.xml.old");
            f.renameTo(new File("resource.xml"));
            
            return true;

        } catch (TransactionXmlLoggerException ex) {
            ex.printStackTrace();
        }
        
        return false;
    }

    public boolean canCommit(String tID)
    {
        if (logger == null)
            logger = new TransactionXmlLogger("translog", tID);

        try {
            logger.setTransactionStatus(TransactionXmlLogger.TransactionStatus.TRANSACTION_2PC);

            return true;

        } catch (TransactionXmlLoggerException ex) {
            ex.printStackTrace();
        }
        return false;
    }

    public boolean doCommit(String tID)
    {
        if (logger == null)
            logger = new TransactionXmlLogger("translog", tID);
        
        try {
            logger.setTransactionStatus(TransactionXmlLogger.TransactionStatus.TRANSACTION_COMMITTED);

            /*String oldFile = logger.getValueAfter("Resource");
            File f = new File(oldFile);
            if (f.exists() && f.canWrite())
                f.delete();*/

            return true;

        } catch (TransactionXmlLoggerException ex) {
            ex.printStackTrace();
        }
        return false;
    }

    private String getBestNeighbour()
    {
        return neighbours[0];
    }
}
